<!DOCTYPE html>
<html>

  <head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

  <title>安卓自定义View进阶-Canvas之绘制图形</title>
  <meta name="description" content="自定义View进阶篇，讲解Canvas基础用法，包括，绘制颜色，绘制点，绘制线，绘制矩形，绘制圆角矩形，绘制椭圆，绘制圆，绘制圆弧，以及饼状图示例。">
  <meta name="author" content="GcsSloop">
  <meta name="keywords" content="Canvas, drawColor, drawPoint, drawPoints, drawLine, drawLines, drawRect, drawRoundRect, drawOval, drawCircle, drawArc, PieView, 自定义View详解, 自定义控件, 安卓, Android, CustomView, GcsSloop">
  <meta name="关键字" content="Canvas, drawColor, drawPoint, drawPoints, drawLine, drawLines, drawRect, drawRoundRect, drawOval, drawCircle, drawArc, PieView, 自定义View详解, 自定义控件, 安卓, Android, CustomView, GcsSloop">
  

  <meta name="twitter:card" content="summary">
  <meta name="twitter:title" content="安卓自定义View进阶-Canvas之绘制图形">
  <meta name="twitter:description" content="自定义View进阶篇，讲解Canvas基础用法，包括，绘制颜色，绘制点，绘制线，绘制矩形，绘制圆角矩形，绘制椭圆，绘制圆，绘制圆弧，以及饼状图示例。">
  <meta name="twitter:keywords" content="Canvas, drawColor, drawPoint, drawPoints, drawLine, drawLines, drawRect, drawRoundRect, drawOval, drawCircle, drawArc, PieView, 自定义View详解, 自定义控件, 安卓, Android, CustomView, GcsSloop">
  
  <meta property="og:type" content="article">
  <meta property="og:title" content="安卓自定义View进阶-Canvas之绘制图形">
  <meta property="og:description" content="自定义View进阶篇，讲解Canvas基础用法，包括，绘制颜色，绘制点，绘制线，绘制矩形，绘制圆角矩形，绘制椭圆，绘制圆，绘制圆弧，以及饼状图示例。">
  <meta name="og:keywords" content="Canvas, drawColor, drawPoint, drawPoints, drawLine, drawLines, drawRect, drawRoundRect, drawOval, drawCircle, drawArc, PieView, 自定义View详解, 自定义控件, 安卓, Android, CustomView, GcsSloop">

  <meta name="theme-color" content="#343434">
  
  <link rel="icon" type="image/png" href="https://raw.githubusercontent.com/GcsSloop/gcssloop.github.io/master/assets/siteinfo/favicon.png" />
  <link href="https://raw.githubusercontent.com/GcsSloop/gcssloop.github.io/master/assets/siteinfo/favicon.png" rel="shortcut icon" type="image/png">
  
  <link href="//netdna.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css" rel="stylesheet">
  <link rel="stylesheet" href="/css/main.css">

  <link rel="canonical" href="http://www.gcssloop.com/customview/Canvas_BasicGraphics">
  <link rel="alternate" type="application/rss+xml" title="GcsSloop" href="http://www.gcssloop.com/feed.xml">
  
  <meta name="google-site-verification" content="Z_g58PkzRAyBMxkqrcDdWrTBK8oOWM-7rUHauhLNF2E" />
  <meta name="baidu-site-verification" content="kUtTXCKaZs" />
  <meta name="baidu-site-verification" content="6DuDv3aaJX" />
  
  <!--阅读次数统计-->
  <script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"> </script>
  
  <!--Fuck Weixin and Baidu-->
  <meta http-equiv="Cache-Control" content="no-transform">
  <meta http-equiv=”Cache-Control” content=”no-siteapp” />
  <meta name="applicable-device" content="pc,mobile">
  <meta name="HandheldFriendly" content="true"/>

  <!-- Google Ad -->
  <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
  <script>
    (adsbygoogle = window.adsbygoogle || []).push({
      google_ad_client: "ca-pub-2767831356529421",
      enable_page_level_ads: true
    });
  </script>

</head>


  <body>

    <span class="mobile btn-mobile-menu">
        <i class="fa fa-list btn-mobile-menu__icon"></i>
        <i class="fa fa-angle-up btn-mobile-close__icon hidden"></i>
    </span>
    
    <header class="panel-cover panel-cover--collapsed" style="background-image: url('/assets/siteinfo/background-cover.jpg')">
  <div class="panel-main">

    <div class="panel-main__inner panel-inverted">
    <div class="panel-main__content">

        <a href="/#blog" title="前往 GcsSloop 的主页" class="blog-button"><img src="/assets/siteinfo/avatar.jpg" width="80" alt="GcsSloop logo" class="panel-cover__logo logo" /></a>
        <h1 class="panel-cover__title panel-title"><a href="/#blog" title="link to homepage for GcsSloop" class="blog-button">GcsSloop</a></h1>

        
        <span class="panel-cover__subtitle panel-subtitle">Just do IT later.</span>
        
        <hr class="panel-cover__divider" />
        <p class="panel-cover__description">嗨，我是 GcsSloop，一名来自2.5次元的魔法师，Android自定义View系列文章作者，非著名程序员。</p>
        <hr class="panel-cover__divider panel-cover__divider--secondary" />
        
        
        <p class="panel-cover__description">欢迎来到我的魔法世界!</p>
        
        
        <div class="navigation-wrapper">
          <div>
            <nav class="cover-navigation cover-navigation--primary">
              <ul class="navigation">
                <li class="navigation__item"><a href="/#blog" title="访问博客" class="blog-button">博客</a></li>
                
                  
                    <li class="navigation__item"><a href="https://github.com/GcsSloop" target="_blank" title="GcsSloop's GitHub">GitHub</a></li>
                  
                  
                
                  
                    <li class="navigation__item"><a href="/timeline" title="博客目录">目录</a></li>
                  
                  
                
                  
                    <li class="navigation__item"><a href="https://xiaozhuanlan.com/u/GcsSloop" target="_blank" title="小专栏">专栏</a></li>
                  
                  
                
                  
                    <li class="navigation__item"><a href="/category/customview" title="自定义View教程目录">自定义控件</a></li>
                  
                  
                
                  
                    <li class="navigation__item"><a href="/friends" title="友链">友链</a></li>
                  
                  
                
              </ul>
            </nav>
          </div>
          
          <div><nav class="cover-navigation navigation--social">
  <ul class="navigation">

  
  <!-- Weibo -->
  <li class="navigation__item">
    <a href="http://weibo.com/GcsSloop" title="@GcsSloop 的微博" target="_blank">
      <i class='social fa fa-weibo'></i>
      <span class="label">Weibo</span>
    </a>
  </li>
  

  
  <!-- Github -->
  <li class="navigation__item">
    <a href="https://github.com/GcsSloop" title="@GcsSloop 的 Github" target="_blank">
      <i class='social fa fa-github'></i>
      <span class="label">Github</span>
    </a>
  </li>
  
  
  
  <!-- Twitter -->
  <li class="navigation__item">
    <a href="http://twitter.com/GcsSloop" title="@GcsSloop" target="_blank">
      <i class='social fa fa-twitter'></i>
      <span class="label">Twitter</span>
    </a>
  </li>
  

    

  

  
  <!-- RSS -->
  <li class="navigation__item">
    <a href="/feed.xml" rel="author" title="RSS" target="_blank">
      <i class='social fa fa-rss'></i>
      <span class="label">RSS</span>
    </a>
  </li>
  

  
  <!-- Email -->
  <li class="navigation__item">
    <a href="mailto:GcsSloop@gmail.com" title="发邮件给我">
      <i class='social fa fa-envelope'></i>
      <span class="label">Email</span>
    </a>
  </li>
  

  
  <!-- Copyright -->
  <li class="navigation__item">
    <a href="http://choosealicense.online" title="选择版权"  target="_blank">
      <i class="social fa fa-copyright"></i>
      <span class="label">版权</span>
    </a>
  </li>
  
  
  </ul>
</nav>
</div>
        </div>
      </div>
    </div>
    
    
    <div class="panel-cover--overlay cover-slate"></div>
    
  </div>
</header>


    <div class="content-wrapper">
        <div class="content-wrapper__inner">
            <article class="post-container post-container--single" itemscope itemtype="http://schema.org/BlogPosting">
  <header class="post-header">
    <div class="post-meta" style="font-size:.8em">
      <time datetime="2016-01-21 00:00:00 +0800" itemprop="datePublished" class="post-meta__date date">2016-01-21</time> &#8226; <span class="post-meta__tags tags">自定义View,Canvas</span> &#8226; View <span id="busuanzi_value_page_pv"></span> times.
</span>
    </div>
    <h1 class="post-title">安卓自定义View进阶-Canvas之绘制图形</h1>
  </header>

  <section class="post">
    <p>在上一篇<a href="http://www.gcssloop.com/customview/CustomViewProcess">自定义View分类与流程</a>中我们了解自定义View相关的基本知识，不过，这些东西依旧还是理论，并不能<strong>拿来(zhuang)用(B)</strong>, 这一次我们就了解一些<strong>能(zhaung)用(B)</strong>的东西。</p>

<p>在本篇文章中，我们先了解Canvas的基本用法，最后用一个小示例来结束本次教程。</p>

<h2 id="一canvas简介">一.Canvas简介</h2>

<p>Canvas我们可以称之为画布，能够在上面绘制各种东西，是安卓平台2D图形绘制的基础，非常强大。</p>

<p><strong>一般来说，比较基础的东西有两大特点:</strong></p>

<ul>
  <li>1.可操作性强：由于这些是构成上层的基础，所以可操作性必然十分强大。</li>
  <li>2.比较难用：各种方法太过基础，想要完美的将这些操作组合起来有一定难度。</li>
</ul>

<p>不过不必担心，本系列文章不仅会介绍到Canvas的操作方法，还会简单介绍一些设计思路和技巧。</p>

<h2 id="二canvas的常用操作速查表">二.Canvas的常用操作速查表</h2>

<table>
  <thead>
    <tr>
      <th>操作类型</th>
      <th>相关API</th>
      <th>备注</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>绘制颜色</td>
      <td>drawColor, drawRGB, drawARGB</td>
      <td>使用单一颜色填充整个画布</td>
    </tr>
    <tr>
      <td>绘制基本形状</td>
      <td>drawPoint, drawPoints, drawLine, drawLines, drawRect, drawRoundRect, drawOval, drawCircle, drawArc</td>
      <td>依次为 点、线、矩形、圆角矩形、椭圆、圆、圆弧</td>
    </tr>
    <tr>
      <td>绘制图片</td>
      <td>drawBitmap, drawPicture</td>
      <td>绘制位图和图片</td>
    </tr>
    <tr>
      <td>绘制文本</td>
      <td>drawText,    drawPosText, drawTextOnPath</td>
      <td>依次为 绘制文字、绘制文字时指定每个文字位置、根据路径绘制文字</td>
    </tr>
    <tr>
      <td>绘制路径</td>
      <td>drawPath</td>
      <td>绘制路径，绘制贝塞尔曲线时也需要用到该函数</td>
    </tr>
    <tr>
      <td>顶点操作</td>
      <td>drawVertices, drawBitmapMesh</td>
      <td>通过对顶点操作可以使图像形变，drawVertices直接对画布作用、 drawBitmapMesh只对绘制的Bitmap作用</td>
    </tr>
    <tr>
      <td>画布剪裁</td>
      <td>clipPath,    clipRect</td>
      <td>设置画布的显示区域</td>
    </tr>
    <tr>
      <td>画布快照</td>
      <td>save, restore, saveLayerXxx, restoreToCount, getSaveCount</td>
      <td>依次为 保存当前状态、 回滚到上一次保存的状态、 保存图层状态、 回滚到指定状态、 获取保存次数</td>
    </tr>
    <tr>
      <td>画布变换</td>
      <td>translate, scale, rotate, skew</td>
      <td>依次为 位移、缩放、 旋转、错切</td>
    </tr>
    <tr>
      <td>Matrix(矩阵)</td>
      <td>getMatrix, setMatrix, concat</td>
      <td>实际上画布的位移，缩放等操作的都是图像矩阵Matrix， 只不过Matrix比较难以理解和使用，故封装了一些常用的方法。</td>
    </tr>
  </tbody>
</table>

<blockquote>
  <p>PS： Canvas常用方法在上面表格中已经全部列出了，当然还存在一些其他的方法未列出，具体可以参考官方文档 <a href="http://developer.android.com/reference/android/graphics/Canvas.html">Canvas</a></p>
</blockquote>

<hr />

<h2 id="三canvas详解">三.Canvas详解</h2>

<p>本篇内容主要讲解如何利用Canvas绘制基本图形。</p>

<h3 id="绘制颜色">绘制颜色：</h3>

<p>绘制颜色是填充整个画布，常用于绘制底色。</p>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="n">canvas</span><span class="o">.</span><span class="na">drawColor</span><span class="o">(</span><span class="n">Color</span><span class="o">.</span><span class="na">BLUE</span><span class="o">);</span> <span class="c1">//绘制蓝色</span>
</code></pre>
</div>

<p><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/2019-04-29-071434.jpg?gcssloop" alt="" /></p>

<blockquote>
  <p>关于颜色的更多资料请参考<a href="http://www.gcssloop.com/customview/Color/">基础篇_颜色</a></p>
</blockquote>

<hr />

<h3 id="创建画笔">创建画笔：</h3>

<p>要想绘制内容，首先需要先创建一个画笔，如下：</p>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="c1">// 1.创建一个画笔</span>
<span class="kd">private</span> <span class="n">Paint</span> <span class="n">mPaint</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Paint</span><span class="o">();</span>

<span class="c1">// 2.初始化画笔</span>
<span class="kd">private</span> <span class="kt">void</span> <span class="nf">initPaint</span><span class="o">()</span> <span class="o">{</span>
	<span class="n">mPaint</span><span class="o">.</span><span class="na">setColor</span><span class="o">(</span><span class="n">Color</span><span class="o">.</span><span class="na">BLACK</span><span class="o">);</span>       <span class="c1">//设置画笔颜色</span>
	<span class="n">mPaint</span><span class="o">.</span><span class="na">setStyle</span><span class="o">(</span><span class="n">Paint</span><span class="o">.</span><span class="na">Style</span><span class="o">.</span><span class="na">FILL</span><span class="o">);</span>  <span class="c1">//设置画笔模式为填充</span>
	<span class="n">mPaint</span><span class="o">.</span><span class="na">setStrokeWidth</span><span class="o">(</span><span class="mi">10</span><span class="n">f</span><span class="o">);</span>         <span class="c1">//设置画笔宽度为10px</span>
<span class="o">}</span>

<span class="c1">// 3.在构造函数中初始化</span>
<span class="kd">public</span> <span class="nf">SloopView</span><span class="o">(</span><span class="n">Context</span> <span class="n">context</span><span class="o">,</span> <span class="n">AttributeSet</span> <span class="n">attrs</span><span class="o">)</span> <span class="o">{</span>
   <span class="kd">super</span><span class="o">(</span><span class="n">context</span><span class="o">,</span> <span class="n">attrs</span><span class="o">);</span>
   <span class="n">initPaint</span><span class="o">();</span>
<span class="o">}</span>
</code></pre>
</div>

<p>在创建完画笔之后，就可以在Canvas中绘制各种内容了。</p>

<hr />

<h3 id="绘制点">绘制点：</h3>

<p>可以绘制一个点，也可以绘制一组点，如下：</p>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="n">canvas</span><span class="o">.</span><span class="na">drawPoint</span><span class="o">(</span><span class="mi">200</span><span class="o">,</span> <span class="mi">200</span><span class="o">,</span> <span class="n">mPaint</span><span class="o">);</span>     <span class="c1">//在坐标(200,200)位置绘制一个点</span>
<span class="n">canvas</span><span class="o">.</span><span class="na">drawPoints</span><span class="o">(</span><span class="k">new</span> <span class="kt">float</span><span class="o">[]{</span>          <span class="c1">//绘制一组点，坐标位置由float数组指定</span>
      <span class="mi">500</span><span class="o">,</span><span class="mi">500</span><span class="o">,</span>
      <span class="mi">500</span><span class="o">,</span><span class="mi">600</span><span class="o">,</span>
      <span class="mi">500</span><span class="o">,</span><span class="mi">700</span>
<span class="o">},</span><span class="n">mPaint</span><span class="o">);</span>
</code></pre>
</div>

<p>关于坐标原点默认在左上角，水平向右为x轴增大方向，竖直向下为y轴增大方向。</p>

<blockquote>
  <p>更多参考这里 <a href="http://www.gcssloop.com/customview/CoordinateSystem/">基础篇_坐标系</a></p>
</blockquote>

<p><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/2019-04-29-071435.jpg?gcssloop" alt="" /></p>

<hr />

<h3 id="绘制直线">绘制直线：</h3>

<p>绘制直线需要两个点，初始点和结束点，同样绘制直线也可以绘制一条或者绘制一组：</p>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="n">canvas</span><span class="o">.</span><span class="na">drawLine</span><span class="o">(</span><span class="mi">300</span><span class="o">,</span><span class="mi">300</span><span class="o">,</span><span class="mi">500</span><span class="o">,</span><span class="mi">600</span><span class="o">,</span><span class="n">mPaint</span><span class="o">);</span>    <span class="c1">// 在坐标(300,300)(500,600)之间绘制一条直线</span>
<span class="n">canvas</span><span class="o">.</span><span class="na">drawLines</span><span class="o">(</span><span class="k">new</span> <span class="kt">float</span><span class="o">[]{</span>               <span class="c1">// 绘制一组线 每四数字(两个点的坐标)确定一条线</span>
    <span class="mi">100</span><span class="o">,</span><span class="mi">200</span><span class="o">,</span><span class="mi">200</span><span class="o">,</span><span class="mi">200</span><span class="o">,</span>
    <span class="mi">100</span><span class="o">,</span><span class="mi">300</span><span class="o">,</span><span class="mi">200</span><span class="o">,</span><span class="mi">300</span>
<span class="o">},</span><span class="n">mPaint</span><span class="o">);</span>
</code></pre>
</div>

<p><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/2019-04-29-071436.jpg?gcssloop" alt="" /></p>

<hr />

<h3 id="绘制矩形">绘制矩形：</h3>

<p>我们都知道，确定一个矩形最少需要四个数据，就是<strong>对角线的两个点</strong>的坐标值，这里一般采用<strong>左上角和右下角</strong>的两个点的坐标。</p>

<p>关于绘制矩形，Canvas提供了三种重载方法，第一种就是提供<strong>四个数值(矩形左上角和右下角两个点的坐标)来确定一个矩形</strong>进行绘制。
其余两种是先将矩形封装为<strong>Rect或RectF</strong>(实际上仍然是用两个坐标点来确定的矩形)，然后传递给Canvas绘制，如下：</p>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="c1">// 第一种</span>
<span class="n">canvas</span><span class="o">.</span><span class="na">drawRect</span><span class="o">(</span><span class="mi">100</span><span class="o">,</span><span class="mi">100</span><span class="o">,</span><span class="mi">800</span><span class="o">,</span><span class="mi">400</span><span class="o">,</span><span class="n">mPaint</span><span class="o">);</span>

<span class="c1">// 第二种</span>
<span class="n">Rect</span> <span class="n">rect</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Rect</span><span class="o">(</span><span class="mi">100</span><span class="o">,</span><span class="mi">100</span><span class="o">,</span><span class="mi">800</span><span class="o">,</span><span class="mi">400</span><span class="o">);</span>
<span class="n">canvas</span><span class="o">.</span><span class="na">drawRect</span><span class="o">(</span><span class="n">rect</span><span class="o">,</span><span class="n">mPaint</span><span class="o">);</span>

<span class="c1">// 第三种</span>
<span class="n">RectF</span> <span class="n">rectF</span> <span class="o">=</span> <span class="k">new</span> <span class="n">RectF</span><span class="o">(</span><span class="mi">100</span><span class="o">,</span><span class="mi">100</span><span class="o">,</span><span class="mi">800</span><span class="o">,</span><span class="mi">400</span><span class="o">);</span>
<span class="n">canvas</span><span class="o">.</span><span class="na">drawRect</span><span class="o">(</span><span class="n">rectF</span><span class="o">,</span><span class="n">mPaint</span><span class="o">);</span>
</code></pre>
</div>

<p>以上三种方法所绘制出来的结果是完全一样的。</p>

<p><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/2019-04-29-071437.jpg?gcssloop" alt="" /></p>

<p>看到这里,相信很多观众会产生一个疑问，<strong>为什么会有Rect和RectF两种？两者有什么区别吗？</strong></p>

<p>答案当然是存在区别的，<strong>两者最大的区别就是精度不同，Rect是int(整形)的，而RectF是float(单精度浮点型)的</strong>。除了精度不同，两种提供的方法也稍微存在差别，在这里我们暂时无需关注，想了解更多参见官方文档 <a href="http://developer.android.com/reference/android/graphics/Rect.html">Rect</a> 和 <a href="http://developer.android.com/reference/android/graphics/RectF.html">RectF</a></p>

<hr />

<h3 id="绘制圆角矩形">绘制圆角矩形：</h3>

<p>绘制圆角矩形也提供了两种重载方式，如下：</p>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="c1">// 第一种</span>
<span class="n">RectF</span> <span class="n">rectF</span> <span class="o">=</span> <span class="k">new</span> <span class="n">RectF</span><span class="o">(</span><span class="mi">100</span><span class="o">,</span><span class="mi">100</span><span class="o">,</span><span class="mi">800</span><span class="o">,</span><span class="mi">400</span><span class="o">);</span>
<span class="n">canvas</span><span class="o">.</span><span class="na">drawRoundRect</span><span class="o">(</span><span class="n">rectF</span><span class="o">,</span><span class="mi">30</span><span class="o">,</span><span class="mi">30</span><span class="o">,</span><span class="n">mPaint</span><span class="o">);</span>

<span class="c1">// 第二种</span>
<span class="n">canvas</span><span class="o">.</span><span class="na">drawRoundRect</span><span class="o">(</span><span class="mi">100</span><span class="o">,</span><span class="mi">100</span><span class="o">,</span><span class="mi">800</span><span class="o">,</span><span class="mi">400</span><span class="o">,</span><span class="mi">30</span><span class="o">,</span><span class="mi">30</span><span class="o">,</span><span class="n">mPaint</span><span class="o">);</span>
</code></pre>
</div>

<p>上面两种方法绘制效果也是一样的，但鉴于第二种方法在API21的时候才添加上，所以我们一般使用的都是第一种。</p>

<p><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/2019-04-29-071438.jpg?gcssloop" alt="" /></p>

<p>下面简单解析一下圆角矩形的几个必要的参数的意思。</p>

<p>很明显可以看出，第二种方法前四个参数和第一种方法的RectF作用是一样的，都是为了确定一个矩形，最后一个参数Paint是画笔，无需多说，<strong>与矩形相比，圆角矩形多出来了两个参数rx 和 ry</strong>，这两个参数是干什么的呢？</p>

<p>稍微分析一下，既然是圆角矩形，他的角肯定是圆弧(圆形的一部分)，<strong>我们一般用什么确定一个圆形呢？</strong></p>

<p>答案是<strong>圆心 和 半径，其中圆心用于确定位置，而半径用于确定大小</strong>。</p>

<p>由于矩形位置已经确定，所以其边角位置也是确定的，那么确定位置的参数就可以省略，只需要用半径就能描述一个圆弧了。</p>

<p>但是，<strong>半径只需要一个参数，但这里怎么会有两个呢？</strong></p>

<p>好吧，让你发现了，<strong>这里圆角矩形的角实际上不是一个正圆的圆弧，而是椭圆的圆弧，这里的两个参数实际上是椭圆的两个半径</strong>，他们看起来个如下图：</p>

<p><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/2019-04-29-071439.jpg?gcssloop" alt="" /></p>

<p><strong>红线标注的 rx 与 ry 就是两个半径，也就是相比绘制矩形多出来的那两个参数。</strong></p>

<p>我们了解到原理后，就可以为所欲为了，通过计算可知我们上次绘制的矩形宽度为700，高度为300，当你让 rx大于350(宽度的一半)， ry大于150(高度的一半) 时奇迹就出现了， 你会发现圆角矩形变成了一个椭圆， 他们画出来是这样的 ( 为了方便确认我更改了画笔颜色， 同时绘制出了矩形和圆角矩形 )：</p>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="c1">// 矩形</span>
<span class="n">RectF</span> <span class="n">rectF</span> <span class="o">=</span> <span class="k">new</span> <span class="n">RectF</span><span class="o">(</span><span class="mi">100</span><span class="o">,</span><span class="mi">100</span><span class="o">,</span><span class="mi">800</span><span class="o">,</span><span class="mi">400</span><span class="o">);</span>  

<span class="c1">// 绘制背景矩形</span>
<span class="n">mPaint</span><span class="o">.</span><span class="na">setColor</span><span class="o">(</span><span class="n">Color</span><span class="o">.</span><span class="na">GRAY</span><span class="o">);</span>
<span class="n">canvas</span><span class="o">.</span><span class="na">drawRect</span><span class="o">(</span><span class="n">rectF</span><span class="o">,</span><span class="n">mPaint</span><span class="o">);</span>

<span class="c1">// 绘制圆角矩形</span>
<span class="n">mPaint</span><span class="o">.</span><span class="na">setColor</span><span class="o">(</span><span class="n">Color</span><span class="o">.</span><span class="na">BLUE</span><span class="o">);</span>
<span class="n">canvas</span><span class="o">.</span><span class="na">drawRoundRect</span><span class="o">(</span><span class="n">rectF</span><span class="o">,</span><span class="mi">700</span><span class="o">,</span><span class="mi">400</span><span class="o">,</span><span class="n">mPaint</span><span class="o">);</span>
</code></pre>
</div>

<p><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/2019-04-29-071440.jpg?gcssloop" alt="" /></p>

<p>其中灰色部分是我们所选定的矩形，而里面的圆角矩形则变成了一个椭圆，<strong>实际上在rx为宽度的一半，ry为高度的一半时，刚好是一个椭圆，通过上面我们分析的原理推算一下就能得到，而当rx大于宽度的一半，ry大于高度的一半时，实际上是无法计算出圆弧的，所以drawRoundRect对大于该数值的参数进行了限制(修正)，凡是大于一半的参数均按照一半来处理。</strong></p>

<hr />

<h3 id="绘制椭圆">绘制椭圆：</h3>
<p>相对于绘制圆角矩形，绘制椭圆就简单的多了，因为他只需要一个矩形作为参数:</p>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="c1">// 第一种</span>
<span class="n">RectF</span> <span class="n">rectF</span> <span class="o">=</span> <span class="k">new</span> <span class="n">RectF</span><span class="o">(</span><span class="mi">100</span><span class="o">,</span><span class="mi">100</span><span class="o">,</span><span class="mi">800</span><span class="o">,</span><span class="mi">400</span><span class="o">);</span>
<span class="n">canvas</span><span class="o">.</span><span class="na">drawOval</span><span class="o">(</span><span class="n">rectF</span><span class="o">,</span><span class="n">mPaint</span><span class="o">);</span>

<span class="c1">// 第二种</span>
<span class="n">canvas</span><span class="o">.</span><span class="na">drawOval</span><span class="o">(</span><span class="mi">100</span><span class="o">,</span><span class="mi">100</span><span class="o">,</span><span class="mi">800</span><span class="o">,</span><span class="mi">400</span><span class="o">,</span><span class="n">mPaint</span><span class="o">);</span>
</code></pre>
</div>

<p>同样，以上两种方法效果完全一样，但一般使用第一种。</p>

<p><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/2019-04-29-071441.jpg?gcssloop" alt="" /></p>

<p>绘制椭圆实际上就是绘制一个矩形的内切图形，原理如下，就不多说了：</p>

<p><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/2019-04-29-071442.jpg?gcssloop" alt="" /></p>

<p>PS： 如果你传递进来的是一个长宽相等的矩形(即正方形)，那么绘制出来的实际上就是一个圆。</p>

<hr />

<h3 id="绘制圆">绘制圆：</h3>

<p>绘制圆形也比较简单, 如下：</p>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="n">canvas</span><span class="o">.</span><span class="na">drawCircle</span><span class="o">(</span><span class="mi">500</span><span class="o">,</span><span class="mi">500</span><span class="o">,</span><span class="mi">400</span><span class="o">,</span><span class="n">mPaint</span><span class="o">);</span>  <span class="c1">// 绘制一个圆心坐标在(500,500)，半径为400 的圆。</span>
</code></pre>
</div>

<p>绘制圆形有四个参数，前两个是圆心坐标，第三个是半径，最后一个是画笔。</p>

<p><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/2019-04-29-071443.jpg?gcssloop" alt="" /></p>

<hr />

<h3 id="绘制圆弧">绘制圆弧：</h3>

<p>绘制圆弧就比较神奇一点了，为了理解这个比较神奇的东西，我们先看一下它需要的几个参数：</p>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="c1">// 第一种</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">drawArc</span><span class="o">(</span><span class="nd">@NonNull</span> <span class="n">RectF</span> <span class="n">oval</span><span class="o">,</span> <span class="kt">float</span> <span class="n">startAngle</span><span class="o">,</span> <span class="kt">float</span> <span class="n">sweepAngle</span><span class="o">,</span> <span class="kt">boolean</span> <span class="n">useCenter</span><span class="o">,</span> <span class="nd">@NonNull</span> <span class="n">Paint</span> <span class="n">paint</span><span class="o">){}</span>
    
<span class="c1">// 第二种</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">drawArc</span><span class="o">(</span><span class="kt">float</span> <span class="n">left</span><span class="o">,</span> <span class="kt">float</span> <span class="n">top</span><span class="o">,</span> <span class="kt">float</span> <span class="n">right</span><span class="o">,</span> <span class="kt">float</span> <span class="n">bottom</span><span class="o">,</span> <span class="kt">float</span> <span class="n">startAngle</span><span class="o">,</span>
            <span class="kt">float</span> <span class="n">sweepAngle</span><span class="o">,</span> <span class="kt">boolean</span> <span class="n">useCenter</span><span class="o">,</span> <span class="nd">@NonNull</span> <span class="n">Paint</span> <span class="n">paint</span><span class="o">)</span> <span class="o">{}</span>
</code></pre>
</div>

<p>从上面可以看出，相比于绘制椭圆，绘制圆弧还多了三个参数：</p>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="n">startAngle</span>  <span class="c1">// 开始角度</span>
<span class="n">sweepAngle</span>  <span class="c1">// 扫过角度</span>
<span class="n">useCenter</span>   <span class="c1">// 是否使用中心</span>
</code></pre>
</div>

<p>通过字面意思我们基本能猜测出来前两个参数(startAngle， sweepAngel)的作用，就是确定角度的起始位置和扫过角度， 不过第三个参数是干嘛的？试一下就知道了,上代码：</p>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="n">RectF</span> <span class="n">rectF</span> <span class="o">=</span> <span class="k">new</span> <span class="n">RectF</span><span class="o">(</span><span class="mi">100</span><span class="o">,</span><span class="mi">100</span><span class="o">,</span><span class="mi">800</span><span class="o">,</span><span class="mi">400</span><span class="o">);</span>
<span class="c1">// 绘制背景矩形</span>
<span class="n">mPaint</span><span class="o">.</span><span class="na">setColor</span><span class="o">(</span><span class="n">Color</span><span class="o">.</span><span class="na">GRAY</span><span class="o">);</span>
<span class="n">canvas</span><span class="o">.</span><span class="na">drawRect</span><span class="o">(</span><span class="n">rectF</span><span class="o">,</span><span class="n">mPaint</span><span class="o">);</span>

<span class="c1">// 绘制圆弧</span>
<span class="n">mPaint</span><span class="o">.</span><span class="na">setColor</span><span class="o">(</span><span class="n">Color</span><span class="o">.</span><span class="na">BLUE</span><span class="o">);</span>
<span class="n">canvas</span><span class="o">.</span><span class="na">drawArc</span><span class="o">(</span><span class="n">rectF</span><span class="o">,</span><span class="mi">0</span><span class="o">,</span><span class="mi">90</span><span class="o">,</span><span class="kc">false</span><span class="o">,</span><span class="n">mPaint</span><span class="o">);</span>

<span class="c1">//-------------------------------------</span>

<span class="n">RectF</span> <span class="n">rectF2</span> <span class="o">=</span> <span class="k">new</span> <span class="n">RectF</span><span class="o">(</span><span class="mi">100</span><span class="o">,</span><span class="mi">600</span><span class="o">,</span><span class="mi">800</span><span class="o">,</span><span class="mi">900</span><span class="o">);</span>
<span class="c1">// 绘制背景矩形</span>
<span class="n">mPaint</span><span class="o">.</span><span class="na">setColor</span><span class="o">(</span><span class="n">Color</span><span class="o">.</span><span class="na">GRAY</span><span class="o">);</span>
<span class="n">canvas</span><span class="o">.</span><span class="na">drawRect</span><span class="o">(</span><span class="n">rectF2</span><span class="o">,</span><span class="n">mPaint</span><span class="o">);</span>

<span class="c1">// 绘制圆弧</span>
<span class="n">mPaint</span><span class="o">.</span><span class="na">setColor</span><span class="o">(</span><span class="n">Color</span><span class="o">.</span><span class="na">BLUE</span><span class="o">);</span>
<span class="n">canvas</span><span class="o">.</span><span class="na">drawArc</span><span class="o">(</span><span class="n">rectF2</span><span class="o">,</span><span class="mi">0</span><span class="o">,</span><span class="mi">90</span><span class="o">,</span><span class="kc">true</span><span class="o">,</span><span class="n">mPaint</span><span class="o">);</span>
</code></pre>
</div>

<p>上述代码实际上是绘制了一个起始角度为0度，扫过90度的圆弧，两者的区别就是是否使用了中心点，结果如下：</p>

<p><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/2019-04-29-071444.jpg?gcssloop" alt="" /></p>

<p>可以发现使用了中心点之后绘制出来类似于一个扇形，而不使用中心点则是圆弧起始点和结束点之间的连线加上圆弧围成的图形。这样中心点这个参数的作用就很明显了，不必多说想必大家试一下就明白了。 另外可以关于角度可以参考一下这篇文章： <a href="http://www.gcssloop.com/customview/AngleAndRadian/">角度与弧度</a></p>

<p>相比于使用椭圆，我们还是使用正圆比较多的，使用正圆展示一下效果：</p>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="n">RectF</span> <span class="n">rectF</span> <span class="o">=</span> <span class="k">new</span> <span class="n">RectF</span><span class="o">(</span><span class="mi">100</span><span class="o">,</span><span class="mi">100</span><span class="o">,</span><span class="mi">600</span><span class="o">,</span><span class="mi">600</span><span class="o">);</span>
<span class="c1">// 绘制背景矩形</span>
<span class="n">mPaint</span><span class="o">.</span><span class="na">setColor</span><span class="o">(</span><span class="n">Color</span><span class="o">.</span><span class="na">GRAY</span><span class="o">);</span>
<span class="n">canvas</span><span class="o">.</span><span class="na">drawRect</span><span class="o">(</span><span class="n">rectF</span><span class="o">,</span><span class="n">mPaint</span><span class="o">);</span>

<span class="c1">// 绘制圆弧</span>
<span class="n">mPaint</span><span class="o">.</span><span class="na">setColor</span><span class="o">(</span><span class="n">Color</span><span class="o">.</span><span class="na">BLUE</span><span class="o">);</span>
<span class="n">canvas</span><span class="o">.</span><span class="na">drawArc</span><span class="o">(</span><span class="n">rectF</span><span class="o">,</span><span class="mi">0</span><span class="o">,</span><span class="mi">90</span><span class="o">,</span><span class="kc">false</span><span class="o">,</span><span class="n">mPaint</span><span class="o">);</span>

<span class="c1">//-------------------------------------</span>

<span class="n">RectF</span> <span class="n">rectF2</span> <span class="o">=</span> <span class="k">new</span> <span class="n">RectF</span><span class="o">(</span><span class="mi">100</span><span class="o">,</span><span class="mi">700</span><span class="o">,</span><span class="mi">600</span><span class="o">,</span><span class="mi">1200</span><span class="o">);</span>
<span class="c1">// 绘制背景矩形</span>
<span class="n">mPaint</span><span class="o">.</span><span class="na">setColor</span><span class="o">(</span><span class="n">Color</span><span class="o">.</span><span class="na">GRAY</span><span class="o">);</span>
<span class="n">canvas</span><span class="o">.</span><span class="na">drawRect</span><span class="o">(</span><span class="n">rectF2</span><span class="o">,</span><span class="n">mPaint</span><span class="o">);</span>

<span class="c1">// 绘制圆弧</span>
<span class="n">mPaint</span><span class="o">.</span><span class="na">setColor</span><span class="o">(</span><span class="n">Color</span><span class="o">.</span><span class="na">BLUE</span><span class="o">);</span>
<span class="n">canvas</span><span class="o">.</span><span class="na">drawArc</span><span class="o">(</span><span class="n">rectF2</span><span class="o">,</span><span class="mi">0</span><span class="o">,</span><span class="mi">90</span><span class="o">,</span><span class="kc">true</span><span class="o">,</span><span class="n">mPaint</span><span class="o">);</span>
</code></pre>
</div>

<p><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/2019-04-29-071445.jpg?gcssloop" alt="" /></p>

<hr />

<h3 id="简要介绍paint">简要介绍Paint</h3>

<p>看了上面这么多，相信有一部分人会产生一个疑问，如果我想绘制一个圆，只要边不要里面的颜色怎么办？</p>

<p>很简单，绘制的<strong>基本形状由Canvas确定</strong>，但绘制出来的<strong>颜色,具体效果则由Paint确定</strong>。</p>

<p>如果你注意到了的话，在一开始我们设置画笔样式的时候是这样的：</p>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="n">mPaint</span><span class="o">.</span><span class="na">setStyle</span><span class="o">(</span><span class="n">Paint</span><span class="o">.</span><span class="na">Style</span><span class="o">.</span><span class="na">FILL</span><span class="o">);</span>  <span class="c1">//设置画笔模式为填充</span>
</code></pre>
</div>

<p>为了展示方便，容易看出效果，之前使用的模式一直为填充模式，实际上画笔有三种模式，如下：</p>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="n">STROKE</span>                <span class="c1">//描边</span>
<span class="n">FILL</span>                  <span class="c1">//填充</span>
<span class="n">FILL_AND_STROKE</span>       <span class="c1">//描边加填充</span>
</code></pre>
</div>

<p>为了区分三者效果我们做如下实验：</p>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="n">Paint</span> <span class="n">paint</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Paint</span><span class="o">();</span>
<span class="n">paint</span><span class="o">.</span><span class="na">setColor</span><span class="o">(</span><span class="n">Color</span><span class="o">.</span><span class="na">BLUE</span><span class="o">);</span>
<span class="n">paint</span><span class="o">.</span><span class="na">setStrokeWidth</span><span class="o">(</span><span class="mi">40</span><span class="o">);</span>     <span class="c1">//为了实验效果明显，特地设置描边宽度非常大</span>

<span class="c1">// 描边</span>
<span class="n">paint</span><span class="o">.</span><span class="na">setStyle</span><span class="o">(</span><span class="n">Paint</span><span class="o">.</span><span class="na">Style</span><span class="o">.</span><span class="na">STROKE</span><span class="o">);</span>
<span class="n">canvas</span><span class="o">.</span><span class="na">drawCircle</span><span class="o">(</span><span class="mi">200</span><span class="o">,</span><span class="mi">200</span><span class="o">,</span><span class="mi">100</span><span class="o">,</span><span class="n">paint</span><span class="o">);</span>

<span class="c1">// 填充</span>
<span class="n">paint</span><span class="o">.</span><span class="na">setStyle</span><span class="o">(</span><span class="n">Paint</span><span class="o">.</span><span class="na">Style</span><span class="o">.</span><span class="na">FILL</span><span class="o">);</span>
<span class="n">canvas</span><span class="o">.</span><span class="na">drawCircle</span><span class="o">(</span><span class="mi">200</span><span class="o">,</span><span class="mi">500</span><span class="o">,</span><span class="mi">100</span><span class="o">,</span><span class="n">paint</span><span class="o">);</span>

<span class="c1">// 描边加填充</span>
<span class="n">paint</span><span class="o">.</span><span class="na">setStyle</span><span class="o">(</span><span class="n">Paint</span><span class="o">.</span><span class="na">Style</span><span class="o">.</span><span class="na">FILL_AND_STROKE</span><span class="o">);</span>
<span class="n">canvas</span><span class="o">.</span><span class="na">drawCircle</span><span class="o">(</span><span class="mi">200</span><span class="o">,</span> <span class="mi">800</span><span class="o">,</span> <span class="mi">100</span><span class="o">,</span> <span class="n">paint</span><span class="o">);</span>
</code></pre>
</div>

<p><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/2019-04-29-071446.jpg?gcssloop" alt="" /></p>

<p>一图胜千言，通过以上实验我们可以比较明显的看出三种模式的区别，如果只需要边缘不需要填充内容的话只需要设置模式为描边(STROKE)即可。</p>

<p>其实关于Paint的内容也是有不少的，这些只是冰山一角，在后续内容中会详细的讲解Paint。</p>

<hr />

<h2 id="小示例">小示例</h2>

<h3 id="简要介绍画布的操作">简要介绍画布的操作:</h3>

<blockquote>
  <p>画布操作详细内容会在下一篇文章中讲解, 不是本文重点，但以下示例中可能会用到，所以此处简要介绍一下。</p>
</blockquote>

<table>
  <thead>
    <tr>
      <th>相关操作</th>
      <th>简要介绍</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>save</td>
      <td>保存当前画布状态</td>
    </tr>
    <tr>
      <td>restore</td>
      <td>回滚到上一次保存的状态</td>
    </tr>
    <tr>
      <td>translate</td>
      <td>相对于当前位置位移</td>
    </tr>
    <tr>
      <td>rotate</td>
      <td>旋转</td>
    </tr>
  </tbody>
</table>

<h3 id="制作一个饼状图">制作一个饼状图</h3>

<p>在展示百分比数据的时候经常会用到饼状图，像这样：</p>

<p><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/2019-04-29-071447.jpg?gcssloop" alt="" /></p>

<h3 id="简单分析">简单分析</h3>

<p>其实根据我们上面的知识已经能自己制作一个饼状图了。不过制作东西最重要的不是制作结果，而是制作思路。
相信我贴上代码大家一看就立刻明白了，非常简单的东西。不过嘛，咱们还是想了解一下制作思路：</p>

<p>先分析饼状图的构成，非常明显，饼状图就是一个又一个的扇形构成的，每个扇形都有不同的颜色，对应的有名字，数据和百分比。</p>

<p>经以上信息可以得出饼状图的最基本数据应包括：<strong>名字 数据值 百分比 对应的角度 颜色</strong>。</p>

<blockquote>
  <p><strong>用户关心的数据 ： 名字 数据值 百分比</strong></p>

  <p><strong>需要程序计算的数据： 百分比 对应的角度</strong></p>

  <p><strong>其中颜色这一项可以用户指定也可以用程序指定(我们这里采用程序指定)。</strong></p>
</blockquote>

<h3 id="封装数据">封装数据：</h3>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">PieData</span> <span class="o">{</span>
    <span class="c1">// 用户关心数据</span>
    <span class="kd">private</span> <span class="n">String</span> <span class="n">name</span><span class="o">;</span>        <span class="c1">// 名字</span>
    <span class="kd">private</span> <span class="kt">float</span> <span class="n">value</span><span class="o">;</span>        <span class="c1">// 数值</span>
    <span class="kd">private</span> <span class="kt">float</span> <span class="n">percentage</span><span class="o">;</span>   <span class="c1">// 百分比</span>
    
    <span class="c1">// 非用户关心数据</span>
    <span class="kd">private</span> <span class="kt">int</span> <span class="n">color</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>      <span class="c1">// 颜色</span>
    <span class="kd">private</span> <span class="kt">float</span> <span class="n">angle</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>    <span class="c1">// 角度</span>

    <span class="kd">public</span> <span class="nf">PieData</span><span class="o">(</span><span class="nd">@NonNull</span> <span class="n">String</span> <span class="n">name</span><span class="o">,</span> <span class="nd">@NonNull</span> <span class="kt">float</span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">name</span> <span class="o">=</span> <span class="n">name</span><span class="o">;</span>
        <span class="k">this</span><span class="o">.</span><span class="na">value</span> <span class="o">=</span> <span class="n">value</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre>
</div>

<p>PS: 以上省略了get set方法</p>

<h3 id="自定义view">自定义View：</h3>

<p>先按照自定义View流程梳理一遍(确定各个步骤应该做的事情)：</p>

<table>
  <thead>
    <tr>
      <th style="text-align: center">步骤</th>
      <th>关键字</th>
      <th>作用</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center">1</td>
      <td>构造函数</td>
      <td>初始化(初始化画笔Paint)</td>
    </tr>
    <tr>
      <td style="text-align: center">2</td>
      <td>onMeasure</td>
      <td>测量View的大小(暂时不用关心)</td>
    </tr>
    <tr>
      <td style="text-align: center">3</td>
      <td>onSizeChanged</td>
      <td>确定View大小(记录当前View的宽高)</td>
    </tr>
    <tr>
      <td style="text-align: center">4</td>
      <td>onLayout</td>
      <td>确定子View布局(无子View，不关心)</td>
    </tr>
    <tr>
      <td style="text-align: center">5</td>
      <td>onDraw</td>
      <td>实际绘制内容(绘制饼状图)</td>
    </tr>
    <tr>
      <td style="text-align: center">6</td>
      <td>提供接口</td>
      <td>提供接口(提供设置数据的接口)</td>
    </tr>
  </tbody>
</table>

<p>代码如下：</p>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">PieView</span> <span class="kd">extends</span> <span class="n">View</span> <span class="o">{</span>
    <span class="c1">// 颜色表 (注意: 此处定义颜色使用的是ARGB，带Alpha通道的)</span>
    <span class="kd">private</span> <span class="kt">int</span><span class="o">[]</span> <span class="n">mColors</span> <span class="o">=</span> <span class="o">{</span><span class="mi">0</span><span class="n">xFFCCFF00</span><span class="o">,</span> <span class="mi">0</span><span class="n">xFF6495ED</span><span class="o">,</span> <span class="mi">0</span><span class="n">xFFE32636</span><span class="o">,</span> <span class="mi">0</span><span class="n">xFF800000</span><span class="o">,</span> <span class="mi">0</span><span class="n">xFF808000</span><span class="o">,</span> <span class="mi">0</span><span class="n">xFFFF8C69</span><span class="o">,</span> <span class="mi">0</span><span class="n">xFF808080</span><span class="o">,</span>
            <span class="mi">0</span><span class="n">xFFE6B800</span><span class="o">,</span> <span class="mi">0</span><span class="n">xFF7CFC00</span><span class="o">};</span>
    <span class="c1">// 饼状图初始绘制角度</span>
    <span class="kd">private</span> <span class="kt">float</span> <span class="n">mStartAngle</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
    <span class="c1">// 数据</span>
    <span class="kd">private</span> <span class="n">ArrayList</span><span class="o">&lt;</span><span class="n">PieData</span><span class="o">&gt;</span> <span class="n">mData</span><span class="o">;</span>
    <span class="c1">// 宽高</span>
    <span class="kd">private</span> <span class="kt">int</span> <span class="n">mWidth</span><span class="o">,</span> <span class="n">mHeight</span><span class="o">;</span>
    <span class="c1">// 画笔</span>
    <span class="kd">private</span> <span class="n">Paint</span> <span class="n">mPaint</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Paint</span><span class="o">();</span>

    <span class="kd">public</span> <span class="nf">PieView</span><span class="o">(</span><span class="n">Context</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">(</span><span class="n">context</span><span class="o">,</span> <span class="kc">null</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nf">PieView</span><span class="o">(</span><span class="n">Context</span> <span class="n">context</span><span class="o">,</span> <span class="n">AttributeSet</span> <span class="n">attrs</span><span class="o">)</span> <span class="o">{</span>
        <span class="kd">super</span><span class="o">(</span><span class="n">context</span><span class="o">,</span> <span class="n">attrs</span><span class="o">);</span>
        <span class="n">mPaint</span><span class="o">.</span><span class="na">setStyle</span><span class="o">(</span><span class="n">Paint</span><span class="o">.</span><span class="na">Style</span><span class="o">.</span><span class="na">FILL</span><span class="o">);</span>
        <span class="n">mPaint</span><span class="o">.</span><span class="na">setAntiAlias</span><span class="o">(</span><span class="kc">true</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="nd">@Override</span>
    <span class="kd">protected</span> <span class="kt">void</span> <span class="nf">onSizeChanged</span><span class="o">(</span><span class="kt">int</span> <span class="n">w</span><span class="o">,</span> <span class="kt">int</span> <span class="n">h</span><span class="o">,</span> <span class="kt">int</span> <span class="n">oldw</span><span class="o">,</span> <span class="kt">int</span> <span class="n">oldh</span><span class="o">)</span> <span class="o">{</span>
        <span class="kd">super</span><span class="o">.</span><span class="na">onSizeChanged</span><span class="o">(</span><span class="n">w</span><span class="o">,</span> <span class="n">h</span><span class="o">,</span> <span class="n">oldw</span><span class="o">,</span> <span class="n">oldh</span><span class="o">);</span>
        <span class="n">mWidth</span> <span class="o">=</span> <span class="n">w</span><span class="o">;</span>
        <span class="n">mHeight</span> <span class="o">=</span> <span class="n">h</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="nd">@Override</span>
    <span class="kd">protected</span> <span class="kt">void</span> <span class="nf">onDraw</span><span class="o">(</span><span class="n">Canvas</span> <span class="n">canvas</span><span class="o">)</span> <span class="o">{</span>
        <span class="kd">super</span><span class="o">.</span><span class="na">onDraw</span><span class="o">(</span><span class="n">canvas</span><span class="o">);</span>
        <span class="k">if</span> <span class="o">(</span><span class="kc">null</span> <span class="o">==</span> <span class="n">mData</span><span class="o">)</span>
            <span class="k">return</span><span class="o">;</span>
        <span class="kt">float</span> <span class="n">currentStartAngle</span> <span class="o">=</span> <span class="n">mStartAngle</span><span class="o">;</span>                    <span class="c1">// 当前起始角度</span>
        <span class="n">canvas</span><span class="o">.</span><span class="na">translate</span><span class="o">(</span><span class="n">mWidth</span> <span class="o">/</span> <span class="mi">2</span><span class="o">,</span> <span class="n">mHeight</span> <span class="o">/</span> <span class="mi">2</span><span class="o">);</span>                <span class="c1">// 将画布坐标原点移动到中心位置</span>
        <span class="kt">float</span> <span class="n">r</span> <span class="o">=</span> <span class="o">(</span><span class="kt">float</span><span class="o">)</span> <span class="o">(</span><span class="n">Math</span><span class="o">.</span><span class="na">min</span><span class="o">(</span><span class="n">mWidth</span><span class="o">,</span> <span class="n">mHeight</span><span class="o">)</span> <span class="o">/</span> <span class="mi">2</span> <span class="o">*</span> <span class="mf">0.8</span><span class="o">);</span>  <span class="c1">// 饼状图半径</span>
        <span class="n">RectF</span> <span class="n">rect</span> <span class="o">=</span> <span class="k">new</span> <span class="n">RectF</span><span class="o">(-</span><span class="n">r</span><span class="o">,</span> <span class="o">-</span><span class="n">r</span><span class="o">,</span> <span class="n">r</span><span class="o">,</span> <span class="n">r</span><span class="o">);</span>                     <span class="c1">// 饼状图绘制区域</span>

        <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">mData</span><span class="o">.</span><span class="na">size</span><span class="o">();</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
            <span class="n">PieData</span> <span class="n">pie</span> <span class="o">=</span> <span class="n">mData</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">i</span><span class="o">);</span>
            <span class="n">mPaint</span><span class="o">.</span><span class="na">setColor</span><span class="o">(</span><span class="n">pie</span><span class="o">.</span><span class="na">getColor</span><span class="o">());</span>
            <span class="n">canvas</span><span class="o">.</span><span class="na">drawArc</span><span class="o">(</span><span class="n">rect</span><span class="o">,</span> <span class="n">currentStartAngle</span><span class="o">,</span> <span class="n">pie</span><span class="o">.</span><span class="na">getAngle</span><span class="o">(),</span> <span class="kc">true</span><span class="o">,</span> <span class="n">mPaint</span><span class="o">);</span>
            <span class="n">currentStartAngle</span> <span class="o">+=</span> <span class="n">pie</span><span class="o">.</span><span class="na">getAngle</span><span class="o">();</span>
        <span class="o">}</span>

    <span class="o">}</span>

    <span class="c1">// 设置起始角度</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setStartAngle</span><span class="o">(</span><span class="kt">int</span> <span class="n">mStartAngle</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">mStartAngle</span> <span class="o">=</span> <span class="n">mStartAngle</span><span class="o">;</span>
        <span class="n">invalidate</span><span class="o">();</span>   <span class="c1">// 刷新</span>
    <span class="o">}</span>

    <span class="c1">// 设置数据</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setData</span><span class="o">(</span><span class="n">ArrayList</span><span class="o">&lt;</span><span class="n">PieData</span><span class="o">&gt;</span> <span class="n">mData</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">mData</span> <span class="o">=</span> <span class="n">mData</span><span class="o">;</span>
        <span class="n">initData</span><span class="o">(</span><span class="n">mData</span><span class="o">);</span>
        <span class="n">invalidate</span><span class="o">();</span>   <span class="c1">// 刷新</span>
    <span class="o">}</span>

    <span class="c1">// 初始化数据</span>
    <span class="kd">private</span> <span class="kt">void</span> <span class="nf">initData</span><span class="o">(</span><span class="n">ArrayList</span><span class="o">&lt;</span><span class="n">PieData</span><span class="o">&gt;</span> <span class="n">mData</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">(</span><span class="kc">null</span> <span class="o">==</span> <span class="n">mData</span> <span class="o">||</span> <span class="n">mData</span><span class="o">.</span><span class="na">size</span><span class="o">()</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span>   <span class="c1">// 数据有问题 直接返回</span>
            <span class="k">return</span><span class="o">;</span>

        <span class="kt">float</span> <span class="n">sumValue</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
        <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">mData</span><span class="o">.</span><span class="na">size</span><span class="o">();</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
            <span class="n">PieData</span> <span class="n">pie</span> <span class="o">=</span> <span class="n">mData</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">i</span><span class="o">);</span>

            <span class="n">sumValue</span> <span class="o">+=</span> <span class="n">pie</span><span class="o">.</span><span class="na">getValue</span><span class="o">();</span>       <span class="c1">//计算数值和</span>

            <span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="n">i</span> <span class="o">%</span> <span class="n">mColors</span><span class="o">.</span><span class="na">length</span><span class="o">;</span>       <span class="c1">//设置颜色</span>
            <span class="n">pie</span><span class="o">.</span><span class="na">setColor</span><span class="o">(</span><span class="n">mColors</span><span class="o">[</span><span class="n">j</span><span class="o">]);</span>
        <span class="o">}</span>

        <span class="kt">float</span> <span class="n">sumAngle</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
        <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">mData</span><span class="o">.</span><span class="na">size</span><span class="o">();</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
            <span class="n">PieData</span> <span class="n">pie</span> <span class="o">=</span> <span class="n">mData</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">i</span><span class="o">);</span>

            <span class="kt">float</span> <span class="n">percentage</span> <span class="o">=</span> <span class="n">pie</span><span class="o">.</span><span class="na">getValue</span><span class="o">()</span> <span class="o">/</span> <span class="n">sumValue</span><span class="o">;</span>   <span class="c1">// 百分比</span>
            <span class="kt">float</span> <span class="n">angle</span> <span class="o">=</span> <span class="n">percentage</span> <span class="o">*</span> <span class="mi">360</span><span class="o">;</span>                 <span class="c1">// 对应的角度</span>

            <span class="n">pie</span><span class="o">.</span><span class="na">setPercentage</span><span class="o">(</span><span class="n">percentage</span><span class="o">);</span>                  <span class="c1">// 记录百分比</span>
            <span class="n">pie</span><span class="o">.</span><span class="na">setAngle</span><span class="o">(</span><span class="n">angle</span><span class="o">);</span>                            <span class="c1">// 记录角度大小</span>
            <span class="n">sumAngle</span> <span class="o">+=</span> <span class="n">angle</span><span class="o">;</span>

            <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="s">"angle"</span><span class="o">,</span> <span class="s">""</span> <span class="o">+</span> <span class="n">pie</span><span class="o">.</span><span class="na">getAngle</span><span class="o">());</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre>
</div>

<p><strong>PS: 在更改了数据需要重绘界面时要调用invalidate()这个函数重新绘制。</strong></p>

<h3 id="效果图">效果图</h3>

<p><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/2019-04-29-071448.jpg?gcssloop" alt="" /></p>

<blockquote>
  <p><strong>PS: 这个饼状图并没有添加百分比等数据，仅作为示例使用。</strong></p>
</blockquote>

<h4 id="pieview源码下载"><a href="https://raw.githubusercontent.com/GcsSloop/AndroidNote/master/CustomView/Demo/PieView.zip">PieView源码下载</a></h4>

<h2 id="总结">总结：</h2>

<p>其实自定义View只要按照流程一步步的走，也是比较容易的。不过里面也有不少坑，这些坑还是自己踩过印象比较深，建议大家不要直接copy源码，自己手打体验一下。</p>

<h2 id="about">About</h2>

<p><a href="http://www.gcssloop.com/customview/CustomViewIndex">本系列相关文章</a></p>

<p>作者微博: <a href="http://weibo.com/GcsSloop">GcsSloop</a></p>

<h2 id="参考资料">参考资料：</h2>

<p><a href="http://developer.android.com/reference/android/view/View.html">View</a><br />
<a href="http://developer.android.com/reference/android/graphics/Canvas.html">Canvas</a><br />
<a href="http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1212/703.html">Android Canvas绘图详解</a><br /></p>


    <hr>
  </section>
</article>

<!--广告-->

<!--
<div>
<a href="https://m.aliyun.com/act/team1111/?spm=5176.11533457.1089570.5.424777e3AF8WOJ&userCode=hn5smxtw#/" target="_blank"><img src="/assets/aliyun/1111-980-120.jpg" width="100%"></a>
</div>
-->
<!--捐赠晶石-->
<section class="contribute">
    <script type="text/javascript"> 
      function toggle() {
        var con = document.getElementById("contribute");
        if (con.style.display == "none") {
          con.style.display = "block";
        } else {
          con.style.display = "none";
        }
      }
    </script> 
    <blockquote style="background-color:#F5F5F5; padding: 10px 20px 20px 10px; margin:0px" >
      <h4> 如果你觉得我的文章对你有帮助的话，欢迎赞助一些服务器费用! </h4>
      <p></p>
      <a id=“btn-toggle-contribute” class="btn-contribute" onclick="toggle()" >¥ 点击赞助</a>
      <br>
      <div id="contribute" style="display:none;">
        <p align="center" >
        <img src="/assets/images/wechat.png" alt="微信">
        <img src="/assets/images/alipay.png" alt="支付宝">
        </p>
        <p align="left" >
          <b>感谢所有支持我的魔法师，所有支持过我的魔法师都可以通过微信(GcsSloop)联系我，获赠我的付费专栏！</b>
          <!--
          <a href="/contribute">点击这里查看捐赠者名单。</a>
          -->
        </p>
      </div>
    </blockquote>
</section>
<div>
  <h2>欢迎关注我的微信公众号</h2>
  <img src="/assets/images/banner.jpg" width="100%">
</div>

<!--阅读更多-->
<section class="read-more">
  
  
  <div class="read-more-item">
    <span class="read-more-item-dim">最近的文章</span>
    <h2 class="post-list__post-title post-title"><a href="/customview/Canvas_Convert" title="link to 安卓自定义View进阶-Canvas之画布操作">安卓自定义View进阶-Canvas之画布操作</a></h2>
    <p class="excerpt">上一篇Canvas之绘制基本图形中我们了解了如何使用Canvas绘制基本图形，本次了解一些基本的画布操作。本来想把画布操作放到后面部分的，但是发现很多图形绘制都离不开画布操作，于是先讲解一下画布...&hellip;</p>
    <div class="post-list__meta">
      <time datetime="2016-01-28 00:00:00 +0800" class="post-list__meta--date date">2016-01-28</time> &#8226; <span class="post-list__meta--tags tags">CustomView</span>
      <br/><br/>
      <a style="float:none; margin:0 auto;" class="btn-border-small" href=/customview/Canvas_Convert>继续阅读</a></div>
   </div>
   
   
   
   
   <div class="read-more-item">
       <span class="read-more-item-dim">更早的文章</span>
       <h2 class="post-list__post-title post-title"><a href="/customview/CustomViewProcess" title="link to 安卓自定义View进阶-分类与流程">安卓自定义View进阶-分类与流程</a></h2>
       <p class="excerpt">本章节为什么要叫进阶篇？(虽然讲的是基础内容)，因为从本篇开始，将会逐渐揭开自定义View的神秘面纱，每一篇都将比上一篇内容更加深入，利用所学的知识能够制作更加炫酷自定义View，就像在台阶上一...&hellip;</p>
       <div class="post-list__meta">
          <time datetime="2016-01-16 00:00:00 +0800" class="post-list__meta--date date">2016-01-16</time> &#8226; <span class="post-list__meta--tags tags">CustomView</span>
          <br/><br/>
          <a style="float:none; margin:0 auto;" class="btn-border-small" href=/customview/CustomViewProcess>继续阅读</a>
       </div>
   </div>
   
</section>

<!--网易云跟帖-->
<!--
<div id="cloud-tie-wrapper" class="cloud-tie-wrapper"></div>
<script src="https://img1.cache.netease.com/f2e/tie/yun/sdk/loader.js"></script>
<script>
var cloudTieConfig = {
  url: document.location.href, 
  sourceId: "",
  productKey: "a85dba2840134721a7b69a15b2e0f217",
  target: "cloud-tie-wrapper"
};
var yunManualLoad = true;
Tie.loader("aHR0cHM6Ly9hcGkuZ2VudGllLjE2My5jb20vcGMvbGl2ZXNjcmlwdC5odG1s", true);
</script>
-->

<style type="text/css">
.isso-comment > div.avatar {
    border: 0px;
    box-shadow: none;
    display: block;
    float: left;
    width: 7%;
    margin: 3px 15px 0 0;
}
.isso-postbox > .form-wrapper > .auth-section .post-action > input {
    border-radius: 6px;
    padding: 6px;
    padding-left: 16px;
    padding-right: 16px;
    border: 1px solid #CCC;
    background-color: #D58D44;
    cursor: pointer;
    outline: 0;
    color: #fff;
    size: 10;
    line-height: 1.4em;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
.isso-postbox > .form-wrapper > .auth-section .post-action > input:hover {
    background-color: #272822;
}
.isso-postbox > .form-wrapper > .auth-section .post-action > input:active {
    background-color: #986530;
}
</style>

<section id="isso-thread"></section>

<script data-isso="//47.52.58.34:1234/"
        data-isso-css="true"
        data-isso-lang="zh"
        data-isso-reply-to-self="false"
        data-isso-require-author="false"
        data-isso-require-email="false"
        data-isso-max-comments-top="10"
        data-isso-max-comments-nested="5"
        data-isso-reveal-on-click="5"
        data-isso-avatar="true"
        data-isso-avatar-bg="#f0f0f0"
        data-isso-avatar-fg="#9abf88 #5698c4 #e279a3 #9163b6 ..."
        data-isso-vote="true"
        data-vote-levels=""
        src="//47.52.58.34:1234/js/embed.min.js">
        </script>

<!--
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
-->
<!-- OneV's Den -->
<!--
<ins class="adsbygoogle"
     style="display:block"
     data-ad-client="ca-pub-3324997515191619"
     data-ad-slot="9170309685"
     data-ad-format="auto"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
-->

            <section class="footer">
    <footer>
    	<span class="footer__copyright">本站点采用<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/deed.zh" target="_blank">知识共享 署名-非商业性使用-禁止演绎 4.0 国际 许可协议</a></span>
        <span class="footer__copyright">本站由 <a href="http://www.GcsSloop.com">@GcsSloop</a> 创建，采用 <a href="https://github.com/GcsSloop/Gcs-Vno-Jekyll" target="_blank">Gcs-Vno-Jekyll</a> 作为主题。<span id="busuanzi_container_site_pv"> 总访问量 <span id="busuanzi_value_site_pv"></span> 次</span> - &copy; 2019</span>
        <span class="footer__sitemap, footer__copyright"><a href="http://www.gcssloop.com/sitemap.xml" target="_blank">Site Map</a>
        <a href="http://www.gcssloop.com/vip" target="_blank">vip</a></span>
    </footer>
</section>

        </div>
    </div>
    
    <script type="text/javascript" src="//code.jquery.com/jquery-1.11.3.min.js"></script>

<script type="text/javascript" src="/js/highlight.pack.js"></script>
<script>hljs.initHighlightingOnLoad();</script>

<script type="text/javascript" src="/js/main.js"></script>

<script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-82493667-1', 'auto');
  ga('send', 'pageview');

</script>

    
  </body>

</html>
